package cn.hum;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author: huangzhibin
 * @Description:
 * @Date: Created in 2021-01-10
 * @Modified By:
 */
public final class RpcClient {

    private static InetSocketAddress registerAddress = new InetSocketAddress("127.0.0.1", 10001);

    private static Map<Class<?>, String> register = new HashMap<>();

    public static void register(Class<?> clazz, String name) {
        register.put(clazz, name);
    }

    public static <T> T getRemoteObject(final Class<T> serviceClass, String hostname, int port) {
        String packageName = register.get(serviceClass);
        if (packageName == null || packageName.trim().length() == 0) return null;

        InetSocketAddress socketAddress = new InetSocketAddress(hostname, port);
        return (T) Proxy.newProxyInstance(serviceClass.getClassLoader(), new Class[]{serviceClass},
                new DyncProxy(socketAddress, packageName));
    }


    private static class DyncProxy implements InvocationHandler {
        private InetSocketAddress socketAddress;
        private String proxyClassName;

        public DyncProxy(InetSocketAddress socketAddress, String proxyClassName) {
            this.socketAddress = socketAddress;
            this.proxyClassName = proxyClassName;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            ObjectOutputStream outputStream = null;
            ObjectInputStream inputStream = null;
            try (Socket socket = new Socket()) {
                socket.connect(registerAddress);
                outputStream = new ObjectOutputStream(socket.getOutputStream());

                //传送给注册中心
                outputStream.writeUTF("execute");
                outputStream.writeUTF(socketAddress.getAddress().getHostAddress());
                outputStream.writeUTF(proxyClassName);
                outputStream.flush();

                //直接使用  socket.getInputStream()，会直接阻塞
                inputStream = new ObjectInputStream(socket.getInputStream());
                //接受结果，看看有没有
                if (!inputStream.readBoolean()) {
                    throw new RuntimeException(String.format("不存在该服务：host[%s]  class[%s]", socketAddress.getHostName(), proxyClassName));
                }

                //传类：上面已经传了
//                outputStream.writeUTF(proxyClassName);
                //传送类的方法
                outputStream.writeUTF(method.getName());
                //传送参数的类类型
                outputStream.writeObject(method.getParameterTypes());
                //传送参数值
                outputStream.writeObject(args);
                //其中会存在缓存，强制全部刷
                outputStream.flush();

                //接受返回结果
                return inputStream.readObject();
            } finally {
                if (outputStream != null) outputStream.close();
                if (inputStream != null) inputStream.close();
            }

        }
    }
}
